using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using FarseerGames.FarseerPhysics;
using BoOnce.Components;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using BoOnce.Helpers;
using BoOnce.GameLogic.Entities;
using BoOnce.Physic;
using BoOnce.GameLogic;
using BoOnce.Xml;

namespace BoOnce.Components
{
    partial class PhysicComponent : GameComponent, IPhysicComponent
    {
        #region Attributes
        private static PhysicComponent _Me = null;
        private IPhysicComponent _ComponentSpecificImpl;

        private float PHYSICAL_ENTITY_MIN_POS_Y;
        private float PHYSICAL_ENTITY_MAX_POS_Y;
        private float PHYSICAL_ENTITY_MIN_POS_X;
        private float PHYSICAL_ENTITY_MAX_POS_X;
        #endregion

        #region Properties
        public static PhysicComponent Instance
        {
            get
            {
                if (_Me != null) { return _Me; }
                else
                {
                    Logger.WriteError("PhysicComponent is not already init !");
                    return null;
                }
            }
        }
        #endregion

        #region Constructor & Initialize
        public PhysicComponent(Game game, PhysicComponentConfigXml config)
            : base(game)
        {
            PHYSICAL_ENTITY_MIN_POS_Y = config.PhysicalEntityMinPosY;
            PHYSICAL_ENTITY_MAX_POS_Y = config.PhysicalEntityMaxPosY;
            PHYSICAL_ENTITY_MIN_POS_X = config.PhysicalEntityMinPosX;
            PHYSICAL_ENTITY_MAX_POS_X = config.PhysicalEntityMaxPosX;

            _Me = this;
            if (config.MultiSimulator)
            {
                _ComponentSpecificImpl = new PhysicComponent_MultiSimulator(game, config);
            }
            else
            {
                _ComponentSpecificImpl = new PhysicComponent_MonoSimulator(game, config);
            }
            CreateLevelBox(_ComponentSpecificImpl.Simulators);
            game.Services.AddService(typeof(PhysicComponent), this);

#if DEBUG
            CameraComponent Camera = (CameraComponent)Game.Services.GetService(typeof(CameraComponent));
            if (Camera == null) Logger.WriteError(" /!\\ Debug Mode need Camera component !!! /!\\");
            DebugComponent debugComponent = new DebugComponent(game, this, Camera);
            debugComponent.Enabled = true;
            Game.Services.AddService(typeof(DebugComponent), debugComponent);
            Game.Components.Add(debugComponent);
#endif
        }

        private void CreateLevelBox(PhysicsSimulator[] simulators)
        {
            float WALL_SIZE = 10f;
            float WALL_HALF_SIZE = 5f;
            float WALL_MASS = 1f;

            float WALL_UP_W = PHYSICAL_ENTITY_MAX_POS_X - PHYSICAL_ENTITY_MIN_POS_X;
            float WALL_UP_H = WALL_SIZE;
            float WALL_UP_X = PHYSICAL_ENTITY_MIN_POS_X + WALL_UP_W/2f;
            float WALL_UP_Y = PHYSICAL_ENTITY_MIN_POS_Y - WALL_HALF_SIZE;

            float WALL_DOWN_W = WALL_UP_W;
            float WALL_DOWN_H = WALL_UP_H;
            float WALL_DOWN_X = PHYSICAL_ENTITY_MIN_POS_X + WALL_DOWN_W/2f;
            float WALL_DOWN_Y = PHYSICAL_ENTITY_MAX_POS_Y + WALL_HALF_SIZE;

            float WALL_LEFT_W = WALL_SIZE;
            float WALL_LEFT_H = PHYSICAL_ENTITY_MAX_POS_Y - PHYSICAL_ENTITY_MIN_POS_Y;
            float WALL_LEFT_X = PHYSICAL_ENTITY_MIN_POS_X - WALL_HALF_SIZE;
            float WALL_LEFT_Y = PHYSICAL_ENTITY_MIN_POS_Y + WALL_LEFT_H/2f;

            float WALL_RIGHT_W = WALL_LEFT_W;
            float WALL_RIGHT_H = WALL_LEFT_H;
            float WALL_RIGHT_X = PHYSICAL_ENTITY_MAX_POS_X + WALL_HALF_SIZE;
            float WALL_RIGHT_Y = PHYSICAL_ENTITY_MIN_POS_Y + WALL_LEFT_H/2f;

            Body upBody = BodyFactory.Instance.CreateRectangleBody(WALL_UP_W, WALL_UP_H,WALL_MASS);
            upBody.IsStatic = true;
            upBody.Position = new Vector2(WALL_UP_X,WALL_UP_Y);
            Geom upGeom = GeomFactory.Instance.CreateRectangleGeom(upBody,WALL_UP_W,WALL_UP_H);

            Body downBody = BodyFactory.Instance.CreateRectangleBody(WALL_DOWN_W, WALL_DOWN_H,WALL_MASS);
            downBody.IsStatic = true;
            downBody.Position = new Vector2(WALL_DOWN_X,WALL_DOWN_Y);
            Geom downGeom = GeomFactory.Instance.CreateRectangleGeom(downBody, WALL_DOWN_W, WALL_DOWN_H);

            Body leftBody = BodyFactory.Instance.CreateRectangleBody(WALL_LEFT_W, WALL_LEFT_H,WALL_MASS);
            leftBody.IsStatic = true;
            leftBody.Position = new Vector2(WALL_LEFT_X,WALL_LEFT_Y);
            Geom leftGeom = GeomFactory.Instance.CreateRectangleGeom(leftBody, WALL_LEFT_W,WALL_LEFT_H);

            Body rightBody = BodyFactory.Instance.CreateRectangleBody(WALL_RIGHT_W, WALL_RIGHT_H,WALL_MASS);
            rightBody.IsStatic = true;
            rightBody.Position = new Vector2(WALL_RIGHT_X,WALL_RIGHT_Y);
            Geom rightGeom = GeomFactory.Instance.CreateRectangleGeom(rightBody,WALL_RIGHT_W,WALL_RIGHT_H);

            foreach (PhysicsSimulator sim in simulators)
            {
                sim.Add(upBody);
                sim.Add(upGeom);
                sim.Add(downBody);
                sim.Add(downGeom);
                sim.Add(leftBody);
                sim.Add(leftGeom);
                sim.Add(rightBody);
                sim.Add(rightGeom);
            }
        }
        #endregion

        #region GameComponent
        public override void Initialize()
        {
            Game.IsFixedTimeStep = true;
            Game.TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 10); //10 ms --> 100 fps for physics update          

            _ComponentSpecificImpl.Initialize();
            Logger.WriteInfo("PhysicsComponent[#" + this.UpdateOrder + "] init");
            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {
            foreach (PhysicsSimulator simulator in _ComponentSpecificImpl.Simulators)
            {
                simulator.Update(gameTime.ElapsedGameTime.Milliseconds * .001f);
            }
            base.Update(gameTime);
        }
        #endregion

        #region IPhysicComponent
        public Ground AddPhysicalEntity(Body body, Geom geom, float height)
        {
            return _ComponentSpecificImpl.AddPhysicalEntity(body, geom, height);
        }

        public void Remove(Body body, Geom geom)
        {
            _ComponentSpecificImpl.Remove(body, geom);
        }

        public Ground NotifyMove(Vector2 newPos, Ground currentGround, Body body, Geom geom, float height, ref float accumulator)
        {
            return _ComponentSpecificImpl.NotifyMove(newPos, currentGround, body, geom, height, ref accumulator);
        }

        public PhysicsSimulator[] Simulators { get { return _ComponentSpecificImpl.Simulators; } }
        #endregion
    }
}
